import os
import re
import sys
import time
import random
import ctypes

import PySide6
import psutil
from PySide6.QtCore import Qt, QRegularExpression, Signal
from PySide6.QtGui import QRegularExpressionValidator, QIcon
from PySide6.QtWidgets import QWidget, QGridLayout, QGroupBox, QVBoxLayout, QFormLayout, QHBoxLayout, QLineEdit, \
    QApplication, \
    QCheckBox, QPushButton, QMessageBox, QDialog, QLabel, QComboBox
import resources.resources_rc  # 导入图片资源不可去
from main_ui.dock_scope import AgeValidator


class Communication_Manage(QWidget):
    e2prom_IP_signal = Signal(int, str, list)

    def __init__(self, parent=None, conn_button=None, config=None):
        super(Communication_Manage, self).__init__()
        # 获取变量
        self.config = config
        self.signal_operat = parent.signal_operat
        self.queue_logging = parent.queue_logging
        self.conn_button = conn_button

        # 创建变量
        self.e2prom_IP_dialog = None
        self.Virtual_network_card_name_list = [                 # 虚拟机名称列表
            'Ethernet',
            'VMware'
        ]

        # 绑定信号
        self.e2prom_IP_signal.connect(self.e2prom_IP_slot)

        # 生成视图
        self.initLayout()
        self.add_info_data()
        self.show()

    def initLayout(self):
        """ 创建 通讯窗口视图 """
        self.setWindowTitle('通讯配置管理')
        # self.setFixedSize(350, 250)
        self.setMinimumHeight(250)
        self.setMinimumWidth(350)
        self.setWindowIcon(QIcon(':/logo.ico'))

        # 创建布局
        self.layout = QVBoxLayout(self)
        self.group_down = QGroupBox('下位机网卡信息配置')
        self.group_upper = QGroupBox('上位机网卡信息配置')
        self.layout.addWidget(self.group_down)
        self.layout.addWidget(self.group_upper)

        """ 下位机配置布局 """
        self.layout_down = QVBoxLayout(self.group_down)
        self.form_layout = QFormLayout()
        self.form_layout.setLabelAlignment(Qt.AlignRight)
        self.button_e2prom_IP_layout = QHBoxLayout()
        self.button_layout = QHBoxLayout()
        self.layout_down.addLayout(self.form_layout)
        self.layout_down.addLayout(self.button_e2prom_IP_layout)
        self.layout_down.addLayout(self.button_layout)

        """ 创建控件 """
        # 输入控件
        self.edit_port1 = QLineEdit()
        self.edit_port1.setValidator(AgeValidator(65535))
        self.edit_port2 = QLineEdit()
        self.edit_port2.setValidator(AgeValidator(65535))
        self.edit_IP = QLineEdit()
        self.edit_IP.setInputMask('000.000.000.000')
        self.edit_SN = QLineEdit()
        self.edit_SN.setInputMask('000.000.000.000')
        self.edit_GW = QLineEdit()
        self.edit_GW.setInputMask('000.000.000.000')
        self.edit_pwd = QLineEdit()
        reg = QRegularExpression('[a-fA-F0-9]+$')
        hex_validator = QRegularExpressionValidator(self)
        hex_validator.setRegularExpression(reg)
        self.edit_pwd.setValidator(hex_validator)
        self.edit_pwd.setMaxLength(8)
        self.edit_pwd.setPlaceholderText('请输入8位16进制字符')
        self.form_layout.addRow('通讯端口1(指令):', self.edit_port1)
        self.form_layout.addRow('通讯端口2(波形):', self.edit_port2)
        self.form_layout.addRow('TCP/IP地址:', self.edit_IP)
        self.form_layout.addRow('子网掩码:', self.edit_SN)
        self.form_layout.addRow('网关:', self.edit_GW)
        self.form_layout.addRow('认证密码:', self.edit_pwd)

        # 按钮控件
        self.button_e2prom_IP = QPushButton('修改E2Prom中的IP地址')
        self.button_e2prom_IP.clicked.connect(self.button_e2prom_IP_slot)
        self.button_test = QPushButton('测试')
        self.button_test.clicked.connect(self.button_test_slot)
        self.button_save = QPushButton('保存')
        self.button_save.clicked.connect(self.button_save_slot)
        self.button_cancel = QPushButton('取消')
        self.button_cancel.clicked.connect(self.close)
        self.button_e2prom_IP_layout.addWidget(self.button_e2prom_IP)
        self.button_layout.addWidget(self.button_test)
        self.button_layout.addWidget(self.button_save)
        # self.button_layout.addWidget(self.button_cancel)

        """ 上位置配置布局 """
        self.comboBox = MyQComboBox()
        self.comboBox.addItems([0, 1, 2, 3])
        self.comboBox.currentIndexChanged.connect(self.combox_index_changed)
        self.comboBox.show_signal.connect(self.comboBox_show_slot)
        self.text_IP = QLineEdit()
        self.text_IP.setInputMask('000.000.000.000')
        self.text_IP.setReadOnly(True)  # 设置为只读
        self.text_IP.setEnabled(False)
        # self.text_IP.setStyleSheet("QLineEdit { border: 1px solid gray; }")


        # 创建自定义验证器
        # ip_validator = IPAddressValidator(self.text_IP)
        # self.text_IP.setValidator(ip_validator)

        self.button_set = QPushButton('设置该静态IP')
        self.button_set.clicked.connect(self.button_set_slot)
        self.button_default = QPushButton('恢复动态IP')
        self.button_default.clicked.connect(self.button_default_slot)

        self.layout_upper = QVBoxLayout(self.group_upper)
        self.form_layout2 = QFormLayout()
        self.form_layout2.setLabelAlignment(Qt.AlignRight)
        self.form_layout2.addRow('         网卡选择:', self.comboBox)
        self.form_layout2.addRow('      TCP/IP地址:', self.text_IP)
        self.button_layout2 = QHBoxLayout()
        self.button_layout2.addWidget(self.button_set)
        self.button_layout2.addWidget(self.button_default)
        self.layout_upper.addLayout(self.form_layout2)
        self.layout_upper.addLayout(self.button_layout2)

    def button_set_slot(self):
        """ 设置静态IP """
        current_text = self.comboBox.currentText()
        ip_address = self.edit_IP.text()
        # 验证是否以管理员身份启动的程序
        if not self.isAdmin():
            QMessageBox.warning(self, '错误', '请以管理员身份启动程序', QMessageBox.Yes)
            return
        # 验证是否符合IP地址标准
        if not self.is_ip_address(ip_address):
            QMessageBox.warning(self, '错误', '请输入正确的IP地址', QMessageBox.Yes)
            return
        subnet = self.edit_SN.text()
        if not self.is_ip_address(subnet):
            QMessageBox.warning(self, '错误', '请输入正确的子网掩码', QMessageBox.Yes)
            return
        gateway = self.edit_GW.text()
        if not self.is_ip_address(gateway):
            QMessageBox.warning(self, '错误', '请输入正确的网关', QMessageBox.Yes)
            return
        dialog = IPInputDialog(initial_ip=ip_address)
        result = dialog.exec()
        if result == QDialog.Accepted:
            ip_address = dialog.get_ip_address()
            result = self.set_interface_IP_static(current_text, ip_address, subnet, gateway)
            if result:
                QMessageBox.information(self, '成功', '设置成功', QMessageBox.Yes)
            else:
                QMessageBox.warning(self, '错误', '设置失败', QMessageBox.Yes)
            self.combox_index_changed(0)
        else:
            # 取消了输入
            pass

    def button_default_slot(self):
        """ 恢复动态IP """
        current_text = self.comboBox.currentText()
        # 验证是否以管理员身份启动的程序
        if not self.isAdmin():
            QMessageBox.warning(self, '错误', '请以管理员身份启动程序', QMessageBox.Yes)
            return
        result = self.set_interface_IP_DHCP(current_text)
        if result:
            QMessageBox.information(self, '成功', '恢复成功', QMessageBox.Yes)
        else:
            QMessageBox.warning(self, '错误', '恢复失败', QMessageBox.Yes)
        self.combox_index_changed(0)

    def combox_index_changed(self, index):
        current_text = self.comboBox.currentText()
        if current_text == '空':
            return
        else:
            address = self.get_interface_address(current_text)
            if address:
                self.text_IP.setText(address)
            else:
                self.text_IP.setText('')
        pass

    def comboBox_show_slot(self):
        """ 刷新列表并重新填充下拉 """
        name_list = self.get_interface_name()
        if name_list:
            self.comboBox.clear()
            self.comboBox.addItems(name_list)
        else:
            self.comboBox.clear()
            self.comboBox.addItems(['空'])

    def get_interface_name(self):
        """ 获取可连接网卡名称 """
        name_list = []
        interfaces = psutil.net_if_addrs()
        interfaces_Info = psutil.net_if_stats()
        for name, values in interfaces.items():
            # 如果未连接就跳过
            add_flag = True  #用于判断是否添加到列表里的标志
            if interfaces_Info[name].isup is not True:
                add_flag = False
            # 判断地址为127.0.0.1 跳过
            for value in values:
                if value.family == 2 and value.address == '127.0.0.1':
                    add_flag = False
            # 如果名字在虚拟机列表里跳过
            for VM_name in self.Virtual_network_card_name_list:
                if VM_name in name:
                    add_flag = False
            if add_flag:
                name_list.append(name)
        return name_list

    def get_interface_address(self, name_input):
        """ 获取某一网卡的ip地址 """
        interfaces = psutil.net_if_addrs()
        for name, values in interfaces.items():
            if name == name_input:
                for value in values:
                    if value.family == 2:
                        return value.address
        return None

    def set_interface_IP_DHCP(self, interface_name):
        """ 设置网卡为DHCP """
        p = os.popen(""" netsh interface ipv4 set address name="{}" source=dhcp """.format(interface_name))
        line = p.read()
        time.sleep(0.2)
        p = os.popen(""" netsh interface ipv4 set address name="{}" source=dhcp """.format(interface_name))
        line = p.read()
        print(line)
        if "已在此接口上启用 DHCP" in line:
            return True
        else:
            return False

    def set_interface_IP_static(self, interface_name, ip_address, subnet, gateway):
        """ 设置网卡为静态IP """
        p = os.popen("""netsh interface ipv4 set address "{}" static {} {} {} 1""".format(interface_name, ip_address, subnet, gateway))
        line = p.read()
        time.sleep(0.2)
        address = self.get_interface_address(interface_name)
        if address == ip_address:
            return True
        else:
            return False

    def is_ip_address(self, ip_address):
        # 验证IP地址标准
        reg = QRegularExpression(
            '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$')
        match = reg.match(ip_address)
        if match.hasMatch():
            return True
        else:
            return False

    def isAdmin(self):
        """ 判断程序是否以管理员身份运行 """
        try:
            return ctypes.windll.shell32.IsUserAnAdmin()
        except:
            return False

    def button_e2prom_IP_slot(self):
        """ 弹出修改e2prom里的IP地址的弹窗 """
        if not self.conn_button.isChecked():
            QMessageBox.information(self, "提示", "请先连接", QMessageBox.Yes)
            return
        self.e2prom_IP_dialog = Dialog_IP(self)
        self.e2prom_IP_dialog.get_e2prom_IP()

    def e2prom_IP_slot(self, flag, str_IP, list_IP):
        """ 传回的e2prom的IP地址 """
        if flag == 0:
            self.signal_operat.emit(2, 2, [str_IP, list_IP])
        elif flag == 1:
            self.signal_operat.emit(2, 4, [])

    def get_e2prom_IP_response(self, IP_list):
        """ 获取e2prom中IP地址的响应 """
        if self.e2prom_IP_dialog:
            self.e2prom_IP_dialog.get_e2prom_IP_response(IP_list)

    def e2prom_IP_response(self, MSG):
        """ 发送e2prom消息之后的响应 """
        if MSG == '写入成功':
            replay = QMessageBox.information(self, '提示', '写入地址成功\n是否重启下位机',
                                             QMessageBox.Yes | QMessageBox.No)
            if replay == QMessageBox.Yes:
                self.signal_operat.emit(2, 3, [None])
            else:
                pass
        else:
            QMessageBox.information(self, '提示', MSG, QMessageBox.Yes)

    def button_test_slot(self):
        """ 通过ping的方式初步判断是否可以联系到地址 """
        # 判断是否处于连接状态
        if self.conn_button.isChecked():
            QMessageBox.information(self, "提示", "请先断开连接", QMessageBox.Yes)
            return
        if self.edit_IP.text():
            p = os.popen("ping " + self.edit_IP.text() + " -n 2")
            line = p.read()
            print(line)
            if "请求超时" in line or "无法访问目标主机" in line or "找不到主机" in line or '传输失败' in line:
                QMessageBox.information(self, "提示", "连接失败", QMessageBox.Yes)
            else:
                reply = QMessageBox.information(self, "提示", "测试成功\n是否保存", QMessageBox.Yes | QMessageBox.No)
                if reply == QMessageBox.Yes:
                    self.button_save_slot()
                else:
                    pass
        else:
            QMessageBox.information(self, "提示", "请输入有效的IP地址", QMessageBox.Yes)

    def button_save_slot(self):
        """ 判断数据是否可用，如果可用提交给主窗口的Tcp_Connect类 """
        # 判断是否处于连接状态
        if self.conn_button.isChecked():
            QMessageBox.information(self, "提示", "请先断开连接", QMessageBox.Yes)
            return
        # 处理数据
        if not self.edit_port1.text():
            QMessageBox.information(self, "提示", "请输入有效的端口1", QMessageBox.Yes)
            return
        elif not self.edit_port2.text():
            QMessageBox.information(self, "提示", "请输入有效的端口2", QMessageBox.Yes)
            return
        elif not self.edit_IP.text():
            QMessageBox.information(self, "提示", "请输入有效IP地址", QMessageBox.Yes)
            return
        elif not self.edit_SN.text():
            QMessageBox.information(self, "提示", "请输入有效的子网掩码", QMessageBox.Yes)
            return
        elif not self.edit_GW.text():
            QMessageBox.information(self, "提示", "请输入有效的网关", QMessageBox.Yes)
            return
        elif not self.edit_pwd.text() or len(self.edit_pwd.text()) != 8:
            QMessageBox.information(self, "提示", "请输入有效的认证密码", QMessageBox.Yes)
            return
        else:
            communication_config = {}
            communication_config['port1'] = self.edit_port1.text()
            communication_config['port2'] = self.edit_port2.text()
            communication_config['IP'] = self.edit_IP.text()
            communication_config['SN'] = self.edit_SN.text()
            communication_config['GW'] = self.edit_GW.text()
            communication_config['password'] = self.edit_pwd.text()
            self.signal_operat.emit(2, 1, [communication_config])
            reply = QMessageBox.information(self, "提示", "保存成功\n是否关闭窗口", QMessageBox.Yes | QMessageBox.No)
            if reply == QMessageBox.Yes:
                self.close()
            else:
                pass

    def update_IP(self, str_IP):
        self.edit_IP.setText(str_IP)
        self.edit_GW.setText(re.sub(r'\.(\d+)$', r'.1', str_IP))

    def add_info_data(self):
        """ 读取以前的信息添加到输入框 """
        if self.config:
            self.edit_port1.setText(self.config['port1'])
            self.edit_port2.setText(self.config['port2'])
            self.edit_IP.setText(self.config['IP'])
            self.edit_SN.setText(self.config['SN'])
            self.edit_GW.setText(self.config['GW'])
            self.edit_pwd.setText(self.config['password'])

    def closeEvent(self, event: PySide6.QtGui.QCloseEvent) -> None:
        """ 重写窗口的关闭事件 """
        super().closeEvent(event)
        self.signal_operat.emit(2, 0, [None])


class Dialog_IP(QDialog):
    def __init__(self, parent=None):
        super(Dialog_IP, self).__init__(parent)
        # 获取变量
        self.config = parent.config
        self.e2prom_IP_signal = parent.e2prom_IP_signal

        # 初始化
        self.layout = QVBoxLayout(self)
        self.initLayout()
        # self.get_e2prom_IP()
        self.show()

    def initLayout(self):
        self.setWindowTitle('修改e2prom中的IP地址')


        self.label = QLabel('注意:\n 修改的是e2prom中的地址，重启生效\n 非必要情况请勿改动!')
        self.layout.addWidget(self.label)
        self.edit_e2prom_IP = QLineEdit()
        # self.edit_e2prom_IP.setInputMask('000.000.000.000')
        reg = QRegularExpression('^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$')  # 单位， 可以有特殊字符
        validator = QRegularExpressionValidator(reg)
        self.edit_e2prom_IP.setValidator(validator)
        self.layout.addWidget(self.edit_e2prom_IP)

        self.button_ok = QPushButton('OK')
        self.button_ok.clicked.connect(self.button_ok_slot)
        self.button_cancel = QPushButton('Cancel')
        self.button_cancel.clicked.connect(self.button_cancel_slot)
        button_layout = QHBoxLayout()
        button_layout.addWidget(self.button_ok)
        button_layout.addWidget(self.button_cancel)
        self.layout.addLayout(button_layout)

    def get_e2prom_IP(self):
        """ 从下位机获取e2prom中的IP地址 """
        self.e2prom_IP_signal.emit(1, '', [])

    def get_e2prom_IP_response(self, IP_list):
        """ 收到获取e2prom中IP地址的响应 """
        ip_text = str(IP_list[0]) + '.' + str(IP_list[1]) + '.' + str(IP_list[2]) + '.' + str(IP_list[3])
        # print(ip_text)
        if ip_text[:8] == '192.168.':
            self.edit_e2prom_IP.setText(ip_text)
        else:
            pass

    def button_ok_slot(self):
        """ ok 按钮的响应 """
        str_IP = self.edit_e2prom_IP.text()
        # print(str_IP)
        if str_IP[:8] == '192.168.':
            list_IP = str_IP.split('.')
            try:
                for i in range(len(list_IP)):
                    list_IP[i] = int(list_IP[i])
            except Exception as e:
                # my_logger.warning('在更改IP地址时，出现无法识别的地址')
                self.queue_logging.put([1, 2, '在更改IP地址时，出现无法识别的地址'])
                print(1947, e)
            # print(list_IP)
            self.e2prom_IP_signal.emit(0, str_IP, list_IP)
        else:
            QMessageBox.information(self, '提示', '输入地址不合法!', QMessageBox.Yes)

        # self.e2prom_IP_signal.emit(self.edit_e2prom_IP.text())
        self.close()

    def button_cancel_slot(self):
        """ 取消的按钮响应 """
        self.close()


class MyQComboBox(QComboBox):
    show_signal = Signal()

    def __init__(self, parent=None):
        super().__init__(parent)

    def showEvent(self, e:PySide6.QtGui.QShowEvent) -> None:
        super().showEvent(e)
        self.show_signal.emit()
    def showPopup(self):
        super().showPopup()
        self.show_signal.emit()


class IPAddressValidator(QRegularExpressionValidator):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setRegularExpression(r"^(?:\d{1,3}\.){0,3}\d{1,3}$")  # 匹配IPv4地址

    def fixup(self, input):
        # 自动添加点分隔符
        parts = []
        for part in input.split('.'):
            if part.isdigit():
                parts.append(part)
            else:
                parts.append('0')  # 如果不是数字，用0代替
        while len(parts) < 4:
            parts.append('0')
        return '.'.join(parts)

# 弹窗输入IP地址尾号的类
class IPInputDialog(QDialog):
    ip_address_changed = Signal(str)  # 定义信号

    def __init__(self, initial_ip="0.0.0.0", parent=None):
        super().__init__(parent)
        self.setWindowTitle("请输入IP地址")
        self.setWindowModality(Qt.WindowModal)  # 设置为模态对话框
        self.setWindowIcon(QIcon(':/logo.ico'))

        text1, text2 = initial_ip.rsplit(".", 1)

        self.ip_line_edit1 = QLineEdit()
        self.ip_line_edit1.setInputMask('000.000.000.')  # 设置输入掩码
        self.ip_line_edit1.setText(text1)
        self.ip_line_edit1.setFixedWidth(80)
        self.ip_line_edit1.setStyleSheet("margin-right: -2px;")
        self.ip_line_edit1.setReadOnly(True)

        self.ip_line_edit2 = QLineEdit()
        # self.ip_line_edit2.setInputMask('000')  # 设置输入掩码
        text3 = text2
        while text3 == text2:
            text3 = str(random.randint(1, 255))


        self.ip_line_edit2.setText(text3)
        self.ip_line_edit2.setFixedWidth(30)
        self.ip_line_edit2.setStyleSheet("margin-left: -2px;")
        regex = r"^(?!" + text2 + ")(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
        validator = QRegularExpressionValidator(QRegularExpression(regex))
        self.ip_line_edit2.setValidator(validator)

        label = QLabel("请再次确定该网卡\n是连接下位机的网卡!")

        # 创建确认按钮
        ok_button = QPushButton("OK")
        ok_button.clicked.connect(self.accept)

        # 创建取消按钮
        cancel_button = QPushButton("Cancel")
        cancel_button.clicked.connect(self.close)

        # 创建布局
        layout_V = QVBoxLayout()
        layout_H1 = QHBoxLayout()
        layout_H2 = QHBoxLayout()
        layout_V.addWidget(label)
        widget_edit = QWidget()
        widget_edit.setFixedWidth(125)
        widget_edit.setLayout(layout_H1)
        layout_V.addWidget(widget_edit)
        layout_V.addLayout(layout_H1)
        layout_V.addLayout(layout_H2)
        layout_H1.addWidget(self.ip_line_edit1)
        layout_H1.addWidget(self.ip_line_edit2)
        layout_H2.addWidget(ok_button)
        layout_H2.addWidget(cancel_button)

        self.setLayout(layout_V)

        self.show()
        self.ip_line_edit2.setFocus()  # 设置 ip_line_edit1 为当前焦点

    def get_ip_address(self):
        # 返回当前输入的IP地址
        return self.ip_line_edit1.text()+self.ip_line_edit2.text()


if __name__ == "__main__":
    # 固定的，PySide6程序都需要QApplication对象。sys.argv是命令行参数列表，确保程序可以双击运行
    app = QApplication(sys.argv)
    # 初始化
    myWin = Communication_Manage()
    # 将窗口控件显示在屏幕上
    myWin.show()
    # 程序运行，sys.exit方法确保程序完整退出。
    sys.exit(app.exec())
